package com.notary.mail.service.impl;

import com.notary.mail.domain.MailVo;
import com.notary.mail.service.IMailService;
import com.notary.mail.util.ImageResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.util.Arrays;
import java.util.Objects;
import java.util.Set;

/**
 * @ClassName: MailServiceImpl
 * @Description: 描述
 * @Author: xiaojl
 * @CreateDate: 2020-05-13 15:50
 * @Version: 1.0
 */
@Service
public class MailServiceImpl implements IMailService {

  public static final String DEFAULT_ENCODING = "UTF-8";
  private final Logger logger = LoggerFactory.getLogger(this.getClass());

  @Autowired
  private JavaMailSender mailSender;
  @Autowired
  private TemplateEngine templateEngine;
  /**
   * 配置文件中我的qq邮箱
   */
  @Value("${spring.mail.from}")
  private String from;

  /**
   * 发送一个简单的文本邮件，可以附带附件：文本邮件发送的基本方法
   */
  @Override
  public void sendSimpleTextMailActual(MailVo mailVo) {
    if (mailVo.getSubject() == null || mailVo.getToWho() == null || mailVo.getToWho().length == 0 || mailVo.getContent() == null) {
      logger.error("邮件-> {} 无法继续执行，因为缺少基本的参数：邮件主题、收件人、邮件内容", mailVo.getSubject());
      throw new RuntimeException("模板邮件无法继续发送，因为缺少必要的参数！");
    }
    logger.info("开始发送简单文本邮件：主题->{}，收件人->{}，抄送人->{}，密送人->{}，附件->{}", mailVo.getSubject(), mailVo.getToWho(), mailVo.getCcPeoples(), mailVo.getBccPeoples(), mailVo.getAttachments());
    if (assertNotNull(mailVo.getAttachments())) {
      try {
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, DEFAULT_ENCODING);
        boolean continueProcess = handleBasicInfo(helper, mailVo, false);
        if (!continueProcess) {
          logger.error("邮件基本信息出错: 主题->{}",  mailVo.getSubject());
          return;
        }
        handleAttachment(helper,  mailVo.getSubject(),  mailVo.getAttachments());
        mailSender.send(mimeMessage);
        logger.info("发送邮件成功: 主题->{}",  mailVo.getSubject());
      } catch (MessagingException e) {
        e.printStackTrace();
        logger.error("发送邮件失败: 主题->{}",  mailVo.getSubject());
      }
    } else {
      SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
      handleBasicInfo(simpleMailMessage, mailVo);
      mailSender.send(simpleMailMessage);
      logger.info("发送邮件成功: 主题->{}", mailVo.getSubject(), mailVo.getContent(), mailVo.getToWho(), mailVo.getCcPeoples(), mailVo.getBccPeoples());
    }
  }

  /**
   * 处理二进制邮件的基本信息，比如需要带附件的文本邮件、HTML文件、图片邮件、模板邮件等等
   * @param mimeMessageHelper：二进制文件的包装类
   * @param isHtml：是否是HTML文件，用于区分带附件的简单文本邮件和真正的HTML文件
   * @return ：返回这个过程中是否出现异常，当出现异常时会取消邮件的发送
   */
  private boolean handleBasicInfo(MimeMessageHelper mimeMessageHelper, MailVo mailVo, boolean isHtml) {
    try {
      mimeMessageHelper.setFrom(from);
      mimeMessageHelper.setSubject(mailVo.getSubject());
      mimeMessageHelper.setText(mailVo.getContent(), isHtml);
      mimeMessageHelper.setTo(mailVo.getToWho());
      if (mailVo.getCcPeoples() != null) {
        mimeMessageHelper.setCc(mailVo.getCcPeoples());
      }
      if (mailVo.getBccPeoples() != null) {
        mimeMessageHelper.setBcc(mailVo.getBccPeoples());
      }

      return true;
    } catch (MessagingException e) {
      e.printStackTrace();
      logger.error("邮件基本信息出错->{}", mailVo.getSubject());
    }
    return false;
  }

  /**
   * 用于填充简单文本邮件的基本信息
   * @param simpleMailMessage：文本邮件信息对象
   */
  private void handleBasicInfo(SimpleMailMessage simpleMailMessage,MailVo mailVo) {
    simpleMailMessage.setFrom(from);
    simpleMailMessage.setSubject(mailVo.getSubject());
    simpleMailMessage.setText(mailVo.getContent());
    simpleMailMessage.setTo(mailVo.getToWho());
    simpleMailMessage.setCc(mailVo.getCcPeoples());
    simpleMailMessage.setBcc(mailVo.getBccPeoples());
  }

  /**
   * 用于处理附件信息，附件需要 MimeMessage 对象
   * @param mimeMessageHelper：处理附件的信息对象
   * @param subject：邮件的主题，用于日志记录
   * @param attachmentFilePaths：附件文件的路径，该路径要求可以定位到本机的一个资源
   */
  private void handleAttachment(MimeMessageHelper mimeMessageHelper, String subject, String[] attachmentFilePaths) {
    if (attachmentFilePaths != null && attachmentFilePaths.length > 0) {
      FileSystemResource resource;
      String fileName;
      for (String attachmentFilePath : attachmentFilePaths) {
        resource = new FileSystemResource(new File(attachmentFilePath));
        if (!resource.exists()) {
          logger.warn("邮件->{} 的附件->{} 不存在！", subject, attachmentFilePath);
          continue;
        }
        fileName = resource.getFilename();
        try {
          mimeMessageHelper.addAttachment(fileName, resource);
        } catch (MessagingException e) {
          e.printStackTrace();
          logger.error("邮件->{} 添加附件->{} 出现异常->{}", subject, attachmentFilePath, e.getMessage());
        }
      }
    }
  }


  /**
   * 可以用来发送带有图片的HTML模板邮件
   * @param mailVo
   */
  @Override
  public void sendHtmlTemplateMailActual(MailVo mailVo) {
    if (mailVo.getSubject() == null || mailVo.getToWho() == null ||  mailVo.getToWho().length == 0 || mailVo.getTemplateName() == null) {
      logger.error("邮件-> {} 无法继续执行，因为缺少基本的参数：邮件主题、收件人、模板名称", mailVo.getSubject());
      throw new RuntimeException("模板邮件无法继续发送，因为缺少必要的参数！");
    }
    logger.info("发送HTML模板邮件：主题->{}，收件人->{}，抄送人->{}，密送人->{}，附件->{}，模板名称->{}，模板解析参数->{}，图片资源->{}）", mailVo.getSubject(), mailVo.getToWho(),mailVo.getCcPeoples(),mailVo.getBccPeoples(), mailVo.getAttachments(), mailVo.getTemplateName(), mailVo.getContext(), mailVo.getImageResourceSet());
    try {
      if (mailVo.getContext() == null) {
         mailVo.setContext(new Context());
        logger.info("邮件->{} 的context为空！", mailVo.getSubject());
      }
      String content = templateEngine.process(mailVo.getTemplateName(), mailVo.getContext());
      MimeMessage mimeMessage = mailSender.createMimeMessage();
      MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, DEFAULT_ENCODING);
      content = handleInLineImageResourceContent(helper, mailVo.getSubject(), content, mailVo.getImageResourceSet());
      mailVo.setContent(content);
      logger.info("解析邮件结果->{}", content);
      boolean continueProcess = handleBasicInfo(helper, mailVo, true);
      if (!continueProcess) {
        logger.error("邮件基本信息出错：主题->{}", mailVo.getSubject());
        return;
      }
      handleInLineImageResource(helper, mailVo.getSubject(), mailVo.getImageResourceSet());
      handleAttachment(helper, mailVo.getSubject(), mailVo.getAttachments());
      mailSender.send(mimeMessage);
      logger.info("发送邮件成功：主题->{}", mailVo.getSubject());
    } catch (MessagingException e) {
      e.printStackTrace();
      logger.error("发送邮件失败：邮件主题->{}", mailVo.getSubject());
    }
  }

  /**
   * 处理内嵌图片的模板HTML邮件，返回一个已经修改过后的HTML字符串
   *
   * @param mimeMessageHelper：邮件信息包装类
   * @param subject：邮件主题
   * @param originContent：模板引擎所解析出来的原始HTML邮件
   * @param imageResourceSet：图片资源集合，用于字符集站位填充
   * @return ：返回处理后的邮件内容
   */
  private String handleInLineImageResourceContent(MimeMessageHelper mimeMessageHelper, String subject, String originContent, Set<ImageResource> imageResourceSet) {
    if (imageResourceSet != null && imageResourceSet.size() > 0) {
      String rscId;
      String resourcePath = null;
      String placeHolder;
      FileSystemResource resource;
      for (ImageResource imageResource : imageResourceSet) {
        rscId = imageResource.getId();
        placeHolder = imageResource.getPlaceholder();
        resourcePath = imageResource.getImageFilePath();
        resource = new FileSystemResource(new File(resourcePath));
        if (!resource.exists()) {
          logger.warn("邮件->{} 内联图片->{} 找不到", subject, resourcePath);
          continue;
        }
        originContent = originContent.replace("\"" + ImageResource.PLACEHOLDERPREFIX + placeHolder + "\"", "\'cid:" + rscId + "\'");
      }
    }
    return originContent;
  }

  /**
   * 填充文本数据，因为数据填充必须在设置基本数据后面进行，所以讲内容和数据的填充进行分离
   *
   * @param mimeMessageHelper
   * @param subject：邮件主题，用于日志记录
   * @param imageResourceSet：资源
   */
  private void handleInLineImageResource(MimeMessageHelper mimeMessageHelper, String subject, Set<ImageResource> imageResourceSet) {
    if (imageResourceSet != null && imageResourceSet.size() > 0) {
      FileSystemResource resource;
      for (ImageResource imageResource : imageResourceSet) {
        resource = new FileSystemResource(new File(imageResource.getImageFilePath()));
        if (!resource.exists()) {
          logger.warn("邮件->{} 的内联图片文件->{} 不存在！", subject, imageResource);
          continue;
        }
        try {
          //添加内联资源
          mimeMessageHelper.addInline(imageResource.getId(), resource);
        } catch (MessagingException e) {
          e.printStackTrace();
          logger.error("邮件->{} 的内联图片文件->{} 添加错误！", subject, imageResource);
        }
      }
    }
  }

  /**
   * 传入的参数不能够为null
   *
   * @param args
   * @return
   */
  private boolean assertNotNull(Object... args) {
    return Arrays.stream(args).noneMatch(Objects::isNull);
  }

}
